﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using StackExchange.Redis;
using OpcRcw.Comn;
using OpcRcw.Da;
using System.Runtime.InteropServices;
using System.Numerics;
using System.Collections;
using System.Xml;
using System.Threading;
using System.Net;

namespace OPC.DA.Redis.Wrapper
{
	public partial class Form1 : Form, IOPCDataCallback
	{
		Type OPCServerType;
		IOPCServer OPCServer;
		Object objOpcGroup = null;

		IOPCAsyncIO2 pIOPCAsyncIO2 = null;                          // instance pointer for asynchronous IO.	
		IConnectionPointContainer pIConnectionPointContainer = null;
		IConnectionPoint pIConnectionPoint = null;

		const int LOCALE_ID = 0x407;  //LOCALE FOR ENGLISH. 
		const int maxTagCount = 50000; //最大数据点数。

		//Tag添加Group定义
		List<OPCITEMDEF> ItemDeffList = new List<OPCITEMDEF>();
		//
		//Tag添加Group返回结果
		List<OPCITEMRESULT> ItemResultList = new List<OPCITEMRESULT>();

		//Tag值,需要更新，使用数组
		OPCITEMSTATE[] ItemValueList = new OPCITEMSTATE[maxTagCount];

		//Tag服务端返回的Handle,同步读时使用
		int[] hOpcServer = null;

        ConnectionMultiplexer redisConn = null;//初始化
        IDatabase redisDb = null;
        string redisGroup = "Opc";
		ConfigurationOptions redisConfig = new ConfigurationOptions();


		AppConfigDaClint appConfigDaClint;
		AppConfigRedis appConfigRedis;

		//Opc 数据更新次数，累计值
		long opcTageUpdateCount = 0;

		public Form1()
		{
			InitializeComponent();
		}
		private void Form1_Load(object sender, EventArgs e)
		{
			LoadAppConfig();

			OPCServerType = System.Type.GetTypeFromProgID(appConfigDaClint.DaServer, appConfigDaClint.Host);

			if (OPCServerType == null)
			{
				MessageBox.Show("找不到OPC服务:" + appConfigDaClint.DaServer, "致命错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
				Application.Exit();
			}

			//订阅失效时，起用同步读定时器。此处仅设置定时周期
			timer2.Interval = appConfigDaClint.RequestedUpdateRate;

			//显示相关的引导信息
			infoBoot.Text = "启动时间：" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss");
			infoOpc.Text = "OPC服务：" + appConfigDaClint.DaServer + " Node：" + appConfigDaClint.Host;

			redisConfig.EndPoints.Add(appConfigRedis.Host, appConfigRedis.Port);
			redisConfig.User = appConfigRedis.User;
			redisConfig.Password = appConfigRedis.Password;
			redisConfig.KeepAlive = appConfigRedis.KeepAlive;
			redisConfig.ConnectTimeout = appConfigRedis.ConnectTimeout;


			//启动线程初始化
			//Thread theader = new Thread(new ThreadStart(new Action(() =>
			//{
			//})));
			//theader.Start();
			RedisStart();
			OpcStartOrReconnect();

        }

		private delegate void InvokeUpdateTextCallback(Label c, string msg);
		//Invoke回调函数
		public void UpdateText(Label c, string text)
		{
			if (c.InvokeRequired)//当前线程不是创建线程
				c.Invoke(new InvokeUpdateTextCallback(UpdateText), new object[] { c, text });//回调
			else//当前线程是创建线程（界面线程）
				c.Text = text;//直接更新
		}
		private void LoadAppConfig()
		{
			string xmlFileName = AppDomain.CurrentDomain.BaseDirectory + "AppConfig.xml";

			XmlDocument xmldoc = new XmlDocument();
			xmldoc.Load(xmlFileName);

			appConfigDaClint = Xml.Xml2Entity<AppConfigDaClint>(xmldoc, "/app/DAClient");

			appConfigRedis = Xml.Xml2Entity<AppConfigRedis>(xmldoc, "/app/Redis");

			redisGroup = appConfigRedis.HashRedisKey;
		}
		private void RedisError(object sender, RedisErrorEventArgs e)
        {
			UpdateText(infoRedis, "RedisError: " + e.EndPoint.ToString() + " ErrMsg" + e.Message);
		}
		private void ConnectionFailed(object sender, ConnectionFailedEventArgs e)
        {
			UpdateText(infoRedis, "Redis ConnectionFailed: " + e.EndPoint.ToString() + "," + e.Exception.InnerException.ToString());
        }
		public void ConnectionRestored(object sender, ConnectionFailedEventArgs e)
        {
			UpdateText(infoRedis, "Redis ConnectionRestored: " + e.EndPoint.ToString());
		}


		private void RedisStart()
		{
			string msg = "Connect Ok!";
			try
			{
				redisConn = null;
				redisDb = null;
				redisConn = ConnectionMultiplexer.Connect(redisConfig);
                redisConn.ErrorMessage += RedisError;
				redisConn.ConnectionFailed += ConnectionFailed;
				redisConn.ConnectionRestored += ConnectionRestored;

				redisDb = redisConn.GetDatabase();
            }
			catch (Exception ex)
			{
				redisDb = null;
				redisConn = null;
				msg = "Connect Fail:" + ex.Message;
			}
			UpdateText(infoRedis, "Redis Host: " + appConfigRedis.Host + ", Port: " + appConfigRedis.Port.ToString() + ", HashKey:" + appConfigRedis.HashRedisKey + ", state: "+ msg);
		}

		private void OpcStartOrReconnect()
		{
			try
            {
                ConnectOpcServer();
				if (OPCServer != null )
                {
					if (ItemDeffList.Count > 0)
					{
						ItemDeffList.Clear();
						//ItemValueList
					}
					BrowserOpcItemIdFromRemote();
					AddGroup();
                }
            }
            catch (Exception ex)
            {
				OpcReConnectClear();

				UpdateText(infoRun, DateTime.Now.ToString() + "  OpcStartOrReconnect: " + ex.Message);
			}
		}
		private void OpcReConnectClear()
		{
				pIConnectionPointContainer = null;
				pIConnectionPoint = null;
				pIOPCAsyncIO2 = null;
				objOpcGroup = null;
				OPCServer = null;
		}
		private void CloseOpc()
		{
			OpcReConnectClear();
		}
		private void CloseRedis()
		{
			try
            {
                if (redisConn != null)
                    redisConn.Close();
                redisConn = null;
                redisDb = null;
            }
            catch(Exception ex)
            {
				UpdateText(infoRun,"CloseRedis: " + ex.Message);
			}
		}

        //批数据更新，数据格式由配置决定
        private void RedisAddBatchHashSet(IBatch dbBatch, string szKey, object Value, short Qualities, OpcRcw.Da.FILETIME TimeStamps)
        {
            dbBatch.HashSetAsync(redisGroup, szKey, Value.ToString());
        }

        //同步读取所有数据
        private void SyncOpcReadAll()
        {
			IOPCSyncIO pIOPCSyncIO = null;
			pIOPCSyncIO = (IOPCSyncIO)objOpcGroup;
			if ( pIOPCSyncIO == null)
				return;

			bool [] isNew =null;
			bool err = true;

			//同步读取所有数据
			try
			{
				IntPtr pItemValues = IntPtr.Zero;
				IntPtr pErrors = IntPtr.Zero;
				pIOPCSyncIO.Read(OPCDATASOURCE.OPC_DS_CACHE, ItemResultList.Count, hOpcServer, out pItemValues, out pErrors);

				int[] errors = new int[ItemDeffList.Count];
				Marshal.Copy(pErrors, errors, 0, ItemResultList.Count);

				isNew = new bool[ItemDeffList.Count];

				IntPtr pos = pItemValues;
				for (int i = 0; i < ItemResultList.Count; i++)
				{
					OPCITEMSTATE state = (OPCITEMSTATE)Marshal.PtrToStructure(pos, typeof(OPCITEMSTATE));
					isNew[i] = (state.ftTimeStamp.dwLowDateTime == ItemValueList[i].ftTimeStamp.dwLowDateTime) ? false : true;
					ItemValueList[i] = state;
					//释放COM对象
					Marshal.DestroyStructure(pos, typeof(OPCITEMSTATE));
					pos = new IntPtr(pos.ToInt32() + Marshal.SizeOf(typeof(OPCITEMSTATE)));
				}
				Marshal.FreeCoTaskMem(pItemValues);
				Marshal.FreeCoTaskMem(pErrors);
				err = false;

				opcTageUpdateCount += ItemResultList.Count;
				UpdateText(infoTagUpdate,DateTime.Now.ToString() + " SyncOpcReadAll : " + ItemResultList.Count.ToString() + ",TotalCount: " + opcTageUpdateCount.ToString());

			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
			}
			finally
            {
				pIOPCSyncIO = null;
			}

			//更新所有数据到Redis
			try
			{
				if ( err == false && redisDb != null )
                {
                    var dbBatch = redisDb.CreateBatch();
                    for (int i = 0; i < ItemResultList.Count; i++)
                    {
                        if (isNew[i] == false)
                            continue;
                        RedisAddBatchHashSet(dbBatch, ItemDeffList[i].szItemID, ItemValueList[i].vDataValue, ItemValueList[i].wQuality, ItemValueList[i].ftTimeStamp);
                    }
                    dbBatch.Execute();
                }
			}
			catch (Exception ex)
			{
                redisDb = null;
                redisConn = null;
                Console.WriteLine(ex.Message);
			}
		}


		[DllImport("ole32.dll", ExactSpelling = true, PreserveSig = false)]
		[return: MarshalAs(UnmanagedType.Interface)]
		static extern object CoCreateInstance(
			[In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
			int pUnkOuter,
			int dwClsContext,
			[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid
		);

		private void ConnectOpcServer()
		{
            try
            {
				OPCServer = (IOPCServer)System.Activator.CreateInstance(OPCServerType);
            }
			catch(Exception ex)
            {
				OPCServer = null;
				UpdateText(infoRun, "ConnectOpcServer: " + ex.Message);
			}
		}
        private int BrowserOpcItemIdFromRemote()
        {
            int dwTagCount = 0;
			IOPCBrowseServerAddressSpace Browser = null;
			IOPCItemProperties ItemProperties = null;
			try
            {
                Browser = (IOPCBrowseServerAddressSpace)OPCServer;
                ItemProperties = (IOPCItemProperties)OPCServer;

                OpcRcw.Da.IEnumString ppIEnumString = null;
                Browser.BrowseOPCItemIDs(OPCBROWSETYPE.OPC_FLAT, "", 0, 0, out ppIEnumString);

                int count = 0;
                string[] rgelt = new string[10];
                while (true)
                {
                    ppIEnumString.RemoteNext(1, rgelt, out count);
                    if (count == 0)
                        break;

                    //szItemID，通常与szItemName一致，不再从.GetItemID获取
                    string szItemID = null;
                    //Browser.GetItemID(rgelt[0], out szItemID);
                    szItemID = rgelt[0];

                    //动态获取OPC Item参数定义，此处直接使用数据ID
                    //ItemProperties.QueryAvailableProperties()
                    //Marshal.Copy(pPropertyIDs,PropertyIDs, 0,dwCount);

                    int dwCount = 1;
                    int[] PropertyIDs = { 1, 2, 3, 4, 5, 6, 7 };
                    IntPtr ppvData = IntPtr.Zero;
                    IntPtr ppErrors = IntPtr.Zero;
                    //仅获取ID=1的数据，对应Tag的数据类型(ID=2,Tag的当前值)
                    ItemProperties.GetItemProperties(szItemID, dwCount, PropertyIDs, out ppvData, out ppErrors);
                    object[] vData = Marshal.GetObjectsForNativeVariants(ppvData, dwCount);

                    Marshal.FreeCoTaskMem(ppvData);
                    Marshal.FreeCoTaskMem(ppErrors);

                    dwTagCount++;

                    OPCITEMDEF def = new OPCITEMDEF();

                    def.szAccessPath = "";                   // Accesspath not needed for this sample
                    def.szItemID = szItemID;       // Item ID, 
                    def.bActive = 1;                     // item is active
                    def.hClient = dwTagCount;                     // client handle	
                    def.dwBlobSize = 0;                    // blob size
                    def.pBlob = IntPtr.Zero;          // pointer to blob 
                    def.vtRequestedDataType = short.Parse(vData[0].ToString());                     // return values in native (cannonical) datatype 
                    ItemDeffList.Add(def);

                    OPCITEMSTATE state = new OPCITEMSTATE();
                    state.hClient = dwTagCount;
                    ItemValueList[dwTagCount - 1] = state;
                }
			}
			catch (Exception ex)
            {
				UpdateText(infoRun,"BrowserOpcItemIdFromRemote: " + ex.Message);
			}
			finally
            {
				Browser = null;
				ItemProperties = null;
			} 

            return dwTagCount;
        }
        
        private void AddGroup()
		{
			bool bSwicthSyncMode = false;
			Guid iidRequiredInterface = typeof(IOPCItemMgt).GUID;
			IOPCGroupStateMgt pIOPCGroupStateMgt = null;

			Int32 hClientGroup = 1;
			Int32 dwRequestedUpdateRate = appConfigDaClint.RequestedUpdateRate;
			Int32 pRevUpdateRate = 100;
			int pSvrGroupHandle = 111;

			//int TimeBias = 0;
			//float deadband = 0;
			//GCHandle hDeadband, pTimeBias;
			//pTimeBias = GCHandle.Alloc(TimeBias, GCHandleType.Pinned);
			//hDeadband = GCHandle.Alloc(deadband, GCHandleType.Pinned);
			//pTimeBias.AddrOfPinnedObject(),
  		    //hDeadband.AddrOfPinnedObject(), 

			try
			{
				OPCServer.AddGroup("__PascalGrp1",
					1,
					dwRequestedUpdateRate,
					hClientGroup,
					IntPtr.Zero,
					IntPtr.Zero,
					LOCALE_ID,
					out pSvrGroupHandle,
					out pRevUpdateRate,
					ref iidRequiredInterface,
					out objOpcGroup);

				if ( appConfigDaClint.RequestedUpdateRate != pRevUpdateRate )
					appConfigDaClint.RequestedUpdateRate = pRevUpdateRate;

				//Query interface for async calls on group object
				pIOPCAsyncIO2 = (IOPCAsyncIO2)objOpcGroup;

				//pIOPCGroupStateMgt = (IOPCGroupStateMgt)objOpcGroup;

				// Query interface for callbacks on group object
				pIConnectionPointContainer = (IConnectionPointContainer)objOpcGroup;

				// Establish Callback for all async operations
				Guid iid = typeof(IOPCDataCallback).GUID;
				pIConnectionPointContainer.FindConnectionPoint(ref iid, out pIConnectionPoint);

				IntPtr pResults;// = IntPtr.Zero;
				IntPtr pErrors;// = IntPtr.Zero;

				// Add items to group
				IOPCItemMgt OPCItemMgt = (IOPCItemMgt)objOpcGroup;
				int ItemCount = ItemDeffList.Count;
				//ItemCount = 2;
				OPCItemMgt.AddItems(ItemCount, ItemDeffList.ToArray(), out pResults, out pErrors);

				// Unmarshal to get the server handles out fom the m_pItemResult 
				// after checking the errors
				int[] errors = new int[ItemCount];
                Marshal.Copy(pErrors, errors, 0, ItemCount);

                IntPtr pos = pResults;
				hOpcServer = new int[ItemCount];
				for ( int i = 0; i < ItemCount; i ++ )
                {
                    OPCITEMRESULT result = (OPCITEMRESULT)Marshal.PtrToStructure(pos, typeof(OPCITEMRESULT));
					ItemResultList.Add(result);
					hOpcServer[i] = result.hServer;
					pos = new IntPtr(pos.ToInt32() + Marshal.SizeOf(typeof(OPCITEMRESULT)));
                }
                Marshal.FreeCoTaskMem(pResults);
				Marshal.FreeCoTaskMem(pErrors);
			}
			catch (Exception ex)
			{
				UpdateText(infoRun,"AddGroup: " + ex.Message);
			}
			try
            {
				// Creates a connection between the OPC servers's connection point and 
				//need IOPCDataCallback
				// this client's sink (the callback object). 
				if (pIConnectionPoint != null )
                {
					Int32 dwCookie = 0;
					pIConnectionPoint.Advise(this, out dwCookie);

					UpdateText(infoRun,"AddGroup: " + "success, TotalTags:"+ ItemDeffList.Count.ToString());
				}
			}
			catch(Exception ex)
            {
				bSwicthSyncMode = true;
				UpdateText(infoRun,"AddGroup: " + ex.Message + " 进入同步读模式");
			}
			if (bSwicthSyncMode )
				timer2.Enabled = true;
			else
				timer1.Enabled = true;
		}

		/// <summary>
		/// Callback IOPCDataCallback OnDataChange event handler implementation.  
		/// This callback function is call by the opc server when any item value is changed.
		/// </summary>
		public virtual void OnDataChange(Int32 dwTransid,
			Int32 hGroup,
			Int32 hrMasterquality,
			Int32 hrMastererror,
			Int32 dwCount,
			int[] phClientItems,
			object[] pvValues,
			short[] pwQualities,
			OpcRcw.Da.FILETIME[] pftTimeStamps,
			int[] pErrors)
		{
			//优先处理OPC订阅数据
			try
			{
				for (int nCount = 0; nCount < dwCount; nCount++)
				{
					//get the values of item 0
					if (pErrors[nCount] == 0)
					{
						//OPCITEMDEF def = ItemDeffList.Find((OPCITEMDEF x) => x.hClient == phClientItems[nCount]);
						ItemValueList[phClientItems[nCount]-1].vDataValue = pvValues[nCount];
					}
				}
			}
			catch (System.Exception ex)
			{
				UpdateText(infoRun,DateTime.Now.ToString() + "  OnDataChange-Runtime Error: " + ex.Message);
			}

			//Redis Batch update，与OnDataChange分开，极端情况下，不影响OnDataChange数据处理
			try
			{
				if (redisConn != null && redisConn.IsConnected)
                {
					var dbBatch = redisDb.CreateBatch();
					for (int nCount = 0; nCount < dwCount; nCount++)
					{
						//get the values of item 0
						if (pErrors[nCount] == 0)
						{
							RedisAddBatchHashSet(dbBatch, ItemDeffList[phClientItems[nCount] - 1].szItemID, ItemValueList[phClientItems[nCount] - 1].vDataValue,
								ItemValueList[phClientItems[nCount] - 1].wQuality, ItemValueList[phClientItems[nCount] - 1].ftTimeStamp);
						}
					}
					dbBatch.Execute();
                }
            }
			catch (System.Exception ex)
			{
                //置空，触发重连
                UpdateText(infoRedis, DateTime.Now.ToString() + "OnDataChange-Runtime Error: Redis Batch update，" + ex.Message);
            }

			opcTageUpdateCount += dwCount;
			UpdateText(infoTagUpdate, DateTime.Now.ToString() + " OnDataChange update: " + dwCount.ToString() + ",TotalCount: " + opcTageUpdateCount.ToString());
		}
		/// <summary>
		/// Callback IOPCDataCallback OnReadComplete event handler implementation.  
		/// This callback function is call by the opc server when async read is compleat.
		/// </summary>
		public virtual void OnReadComplete(System.Int32 dwTransid,
			System.Int32 hGroup,
			System.Int32 hrMasterquality,
			System.Int32 hrMastererror,
			System.Int32 dwCount,
			int[] phClientItems,
			object[] pvValues,
			short[] pwQualities,
			OpcRcw.Da.FILETIME[] pftTimeStamps,
			int[] pErrors)
		{
			if (pErrors[0] == 0)
			{   // Value
				//strAReadVal.Text = String.Format("{0}", pvValues[0]);
				//// Quality
				//strAReadQuality.Text = GetQuality(pwQualities[0]);
				//// Timestamp
				//DateTime dt = ToDateTime(pftTimeStamps[0]);
				//strAReadTimeStp.Text = dt.ToString();
			}
			else
			{
				//String strResult = "";
				//pIOPCServer.GetErrorString(pErrors[0], LOCALE_ID, out strResult);
				//MessageBox.Show(strResult, "Result - OnReadCOmpleate", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}
		/// <summary>
		/// Callback IOPCDataCallback OnWriteComplete event handler implementation.  
		/// This callback function is call by the opc server when async write is compleat.
		/// </summary>
		public virtual void OnWriteComplete(System.Int32 dwTransid,
			System.Int32 hGroup,
			System.Int32 hrMastererr,
			System.Int32 dwCount,
			int[] pClienthandles,
			int[] pErrors)

		{
			//String strResult = "";
			//pIOPCServer.GetErrorString(pErrors[0], LOCALE_ID, out strResult);
			//strWriteResult.Text = strResult;
		}
		/// <summary>
		/// Callback IOPCDataCallback OnCancelComplete event handler implementation.  
		/// <summary>
		public virtual void OnCancelComplete(System.Int32 dwTransid, System.Int32 hGroup)
		{
			// Not implemented in this sample.
		}

		/// <summary>
		/// Converts FILETIME to DateTime
		/// </summary>
		private DateTime ToDateTime(OpcRcw.Da.FILETIME ft)
		{
			long highbuf = (long)ft.dwHighDateTime;
			long buffer = (highbuf << 32) + ft.dwLowDateTime;
			return DateTime.FromFileTime(buffer);
		}
		private String GetQuality(long wQuality)
		{
			String strQuality = "Good";
			if (wQuality != 0)
				strQuality = "Bad";
			//switch (wQuality)
			//{
			//	case Qualities.OPC_QUALITY_GOOD:
			//		strQuality = "Good";
			//		break;
			//	case Qualities.OPC_QUALITY_BAD:
			//		strQuality = "Bad";
			//		break;
			//	case Qualities.OPC_QUALITY_CONFIG_ERROR:
			//		strQuality = "BadConfigurationError";
			//		break;
			//	case Qualities.OPC_QUALITY_NOT_CONNECTED:
			//		strQuality = "BadNotConnected";
			//		break;
			//	case Qualities.OPC_QUALITY_DEVICE_FAILURE:
			//		strQuality = "BadDeviceFailure";
			//		break;
			//	case Qualities.OPC_QUALITY_SENSOR_FAILURE:
			//		strQuality = "BadSensorFailure";
			//		break;
			//	case Qualities.OPC_QUALITY_COMM_FAILURE:
			//		strQuality = "BadCommFailure";
			//		break;
			//	case Qualities.OPC_QUALITY_OUT_OF_SERVICE:
			//		strQuality = "BadOutOfService";
			//		break;
			//	case Qualities.OPC_QUALITY_WAITING_FOR_INITIAL_DATA:
			//		strQuality = "BadWaitingForInitialData";
			//		break;
			//	case Qualities.OPC_QUALITY_EGU_EXCEEDED:
			//		strQuality = "UncertainEGUExceeded";
			//		break;
			//	case Qualities.OPC_QUALITY_SUB_NORMAL:
			//		strQuality = "UncertainSubNormal";
			//		break;
			//	default:
			//		strQuality = "Not handled";
			//		break;
			//}
			return strQuality;
		}

		//Opc服务器状态
		private string[] GetOpcServerStatus()
        {
		
			if (OPCServer != null)
			{
				IntPtr ppServerStatus = IntPtr.Zero;
				OPCServer.GetStatus(out ppServerStatus);

				OPCSERVERSTATUS result = (OPCSERVERSTATUS)Marshal.PtrToStructure(ppServerStatus, typeof(OPCSERVERSTATUS));
				Marshal.FreeCoTaskMem(ppServerStatus);

				List<string> info = new List<string>();
				info.Add("ProgID:         " + appConfigDaClint.DaServer);
				info.Add("StartTime:      " + ToDateTime(result.ftStartTime).ToString("yyyy-MM-dd hh:mm:ss"));
				info.Add("CurrentTime:    " + ToDateTime(result.ftCurrentTime).ToString("yyyy-MM-dd hh:mm:ss"));
				info.Add("LastUpdateTime: " + ToDateTime(result.ftLastUpdateTime).ToString("yyyy-MM-dd hh:mm:ss"));
				info.Add("ServerState:    " + result.dwServerState.ToString());
				info.Add("MajorVersion:   " + result.wMajorVersion.ToString());
				info.Add("MinorVersion:   " + result.wMinorVersion.ToString());
				info.Add("BuildNumber:    " + result.wBuildNumber.ToString());
				info.Add("VendorInfo:     " + result.szVendorInfo);
				return info.ToArray();
			}
			return null;
		}
		//OPC服务是否正常监测
		private void timer1_Tick(object sender, EventArgs e)
        {
			if ( redisConn == null )
            {
				RedisStart();
			}
            try
            {
				if (OPCServer == null )
                {
					OpcStartOrReconnect();
                }
				richTextBox1.Lines = GetOpcServerStatus() ;
            }
			catch(Exception ex)
            {
				OpcReConnectClear();
				UpdateText(infoRun, DateTime.Now.ToString() + " timer1 OpcStartOrReconnect: " + ex.Message);
			}
		}
		//OPC定时同步读取服务
		private void timer2_Tick(object sender, EventArgs e)
        {
		   try
            {
				if (OPCServer == null )
                {
					OpcStartOrReconnect();
                }

				richTextBox1.Lines = GetOpcServerStatus() ;
				SyncOpcReadAll();
			}
			catch (Exception ex)
			{
				OpcReConnectClear();
				UpdateText(infoRun, DateTime.Now.ToString() + " timer1: " + ex.Message);
			}
		}

		//界面监控信息显示
		private void timer3_Tick(object sender, EventArgs e)
        {

        }
    }
}
